Skip to content

🧹 Decouple HTTP exceptions from VAD utility - #11338

Merged
undivisible merged 7 commits into
mainfrom
fix-vad-http-exception-coupling-5064561204901573843
Aug 14, 2026
Merged

🧹 Decouple HTTP exceptions from VAD utility#11338
undivisible merged 7 commits into
mainfrom
fix-vad-http-exception-coupling-5064561204901573843

Conversation

@undivisible

@undivisible undivisible commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

🎯 What: The apply_vad_for_speech_profile utility function in backend/utils/stt/vad.py was directly raising a FastAPI HTTPException(400) when a processed audio file was empty. This has been replaced with a domain-specific VADEmptyError. The HTTPException logic has been moved up to the routing layer in backend/routers/speech_profile.py. The background script backend/scripts/stt/j_apply_vad_to_speech_profiles.py was also updated to catch VADEmptyError.

💡 Why: This change separates concerns and decouples domain/utility logic from web framework-specific exceptions. HTTPException should only be thrown from routing layers. This makes the utility function safer to reuse across different contexts, such as the j_apply_vad_to_speech_profiles.py background script which previously would have crashed upon encountering an empty audio file.

Verification: Ran pytest tests against the updated files (backend/tests/unit/test_speech_profile_wav_decode.py, backend/tests/unit/test_user_speaker_embedding.py, backend/tests/unit/test_vad_onnx.py) to confirm no regressions and tested API compatibility. Code Review was completed.

Result: A cleaner architectural separation of concerns between core utility code and web layers, and a more robust background script that won't crash when encountering empty voice segments.


PR created automatically by Jules for task 5064561204901573843 started by @undivisible

Review in cubic


Note

Low Risk
Layering change with preserved upload 400 semantics; limited to speech-profile VAD paths and covered by new unit tests.

Overview
Speech-profile VAD no longer raises FastAPI errors from the STT utility layer. When VAD finds no speech segments, apply_vad_for_speech_profile now raises a domain VADEmptyError instead of HTTPException(400), and the FastAPI dependency on vad.py is removed.

The POST /v3/upload-audio handler catches VADEmptyError and still returns 400 with detail "Audio is empty", so client behavior for empty/silent uploads stays the same while upload does not proceed to storage.

The batch script j_apply_vad_to_speech_profiles catches VADEmptyError, logs, and skips that user instead of failing the job thread. Unit tests cover the router mapping, batch skip behavior, and the utility raising VADEmptyError.

Reviewed by Cursor Bugbot for commit aeb9888. Configure here.

@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e3840d6f-32c9-432e-b77a-80cf8f24a3c8)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 921c6fcf4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +86 to +87
except VADEmptyError:
raise HTTPException(status_code=400, detail="Audio is empty")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add regression coverage for the new exception boundary

When VAD finds no speech, this commit changes the utility's exception contract and adds translations at the HTTP and batch-script boundaries, but it modifies no tests; an incorrect exception type or catch would therefore turn the upload's intended 400 into a 500 or let the maintenance thread fail unnoticed. Add a behavioral test that drives the zero-segment path and verifies VADEmptyError is translated correctly, as required for behavior-changing bug fixes.

AGENTS.md reference: AGENTS.md:L26-L28

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the zero-segment path is now covered end-to-end — test_empty_vad_returns_400_not_500 drives the real apply_vad_for_speech_profile through the upload route and asserts the 400 (no upload); test_apply_vad_for_speech_profile_raises_for_zero_segments asserts the real utility raises VADEmptyError; test_batch_skips_empty_vad_without_uploading covers the script boundary. All pass (34/34).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Confidence score: 4/5

  • In backend/routers/speech_profile.py, the VADEmptyError400 "Audio is empty" branch is currently untested, so a future change could silently alter this user-facing contract (for example returning a generic error instead of the expected 400). Add a focused router test that triggers VADEmptyError and asserts the exact status code/message to de-risk regressions.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/routers/speech_profile.py">

<violation number="1" location="backend/routers/speech_profile.py:86">
P3: The new `VADEmptyError` handling path is untested. The router branch that converts `VADEmptyError` into the `400 "Audio is empty"` response is the exact user-facing behavior this PR preserves, and the script's `except VADEmptyError: return` is the new robustness behavior that motivated the change — but neither has a regression test (existing tests only cover the WAV decode/duration failure branches, and nothing asserts the empty-audio path). Consider adding a small unit test that patches `apply_vad_for_speech_profile` to raise `VADEmptyError` and asserts the router returns 400 without uploading, and one for the script's early-return-on-empty branch.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

apply_vad_for_speech_profile(file_path)
try:
apply_vad_for_speech_profile(file_path)
except VADEmptyError:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new VADEmptyError handling path is untested. The router branch that converts VADEmptyError into the 400 "Audio is empty" response is the exact user-facing behavior this PR preserves, and the script's except VADEmptyError: return is the new robustness behavior that motivated the change — but neither has a regression test (existing tests only cover the WAV decode/duration failure branches, and nothing asserts the empty-audio path). Consider adding a small unit test that patches apply_vad_for_speech_profile to raise VADEmptyError and asserts the router returns 400 without uploading, and one for the script's early-return-on-empty branch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/routers/speech_profile.py, line 86:

<comment>The new `VADEmptyError` handling path is untested. The router branch that converts `VADEmptyError` into the `400 "Audio is empty"` response is the exact user-facing behavior this PR preserves, and the script's `except VADEmptyError: return` is the new robustness behavior that motivated the change — but neither has a regression test (existing tests only cover the WAV decode/duration failure branches, and nothing asserts the empty-audio path). Consider adding a small unit test that patches `apply_vad_for_speech_profile` to raise `VADEmptyError` and asserts the router returns 400 without uploading, and one for the script's early-return-on-empty branch.</comment>

<file context>
@@ -81,7 +81,10 @@ def upload_profile(file: UploadFile, uid: str = Depends(auth.get_current_user_ui
-    apply_vad_for_speech_profile(file_path)
+    try:
+        apply_vad_for_speech_profile(file_path)
+    except VADEmptyError:
+        raise HTTPException(status_code=400, detail="Audio is empty")
 
</file context>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: test_empty_vad_returns_400_not_500 asserts status 400 + detail "Audio is empty" and no upload; test_batch_skips_empty_vad_without_uploading covers the script early-return branch. Both pass.

@undivisible undivisible added human Human-authored pull request backend Backend Task (python) workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior AI and removed human Human-authored pull request labels Aug 10, 2026
@Git-on-my-level Git-on-my-level removed the workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior label Aug 10, 2026
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Reviewed the full three-file diff; this looks like a good separation-of-concerns cleanup.

  • backend/utils/stt/vad.py: removing the FastAPI import from the VAD utility and replacing the previous HTTPException(400, "Audio is empty") with a domain-specific VADEmptyError keeps the utility layer framework-agnostic. The no-speech branch still exits before trimming/exporting, so the production behavior remains fail-fast for empty VAD results.
  • backend/routers/speech_profile.py: catching VADEmptyError at the upload route boundary correctly preserves the existing client-facing 400 / "Audio is empty" response while keeping the decode, sample-rate, and duration guards ahead of VAD processing.
  • backend/scripts/stt/j_apply_vad_to_speech_profiles.py: catching VADEmptyError and returning from single() means the migration/maintenance script skips empty profiles instead of crashing the batch, while still only re-uploading after the existing duration validation.

Backend CI is green. I also removed workflow-review because this PR does not touch workflow/CI/automation files; the existing backend category is the right primary area. A focused regression test for the new router/script VADEmptyError paths would be a nice follow-up, but I don’t see a production blocker in this diff.

Automated maintainer review; final merge decision remains with the maintainers.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level added the positive-signal Good PR — positive signal, not a formal approval label Aug 10, 2026
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_644ec186-dc8a-499c-849d-3e7693c026c9)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e1665c14c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +145 to +146
) as mock_aseg, patch.object(mod, "VADEmptyError", _FakeVADEmptyError), patch.object(
mod, "apply_vad_for_speech_profile", side_effect=_FakeVADEmptyError("Audio is empty")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exercise the real zero-segment exception boundary

Fresh evidence since the earlier missing-coverage comment is that this new test replaces both VADEmptyError and apply_vad_for_speech_profile with matching fakes, so it never executes the changed zero-segment branch in utils/stt/vad.py. If the real utility continued raising HTTPException or later raised another type, this test would still pass while silent uploads return 500 and the batch script fails to handle them; patch vad_is_empty to return [] and invoke the real utility through the route (or separately assert the real utility's exception) so the regression test covers the production contract.

AGENTS.md reference: AGENTS.md:L43-L45

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the test now patches vad_is_empty to return [] and runs the real apply_vad_for_speech_profile through the route (test_empty_vad_returns_400_not_500), plus a direct utility-level assertion in test_vad_onnx.py. No fakes of the exception or the utility; a wrong exception type in vad.py would now fail the suite.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduces VADEmptyError so empty speech-profile audio returns 400 instead of 500, with a unit test. CI green. Approve-only (refactor, no linked bug issue).

@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks for the update — I reviewed the current four-file diff on this head, including the newly added regression test.

  • backend/utils/stt/vad.py: apply_vad_for_speech_profile() now raises a domain-specific VADEmptyError when VAD returns no speech segments, and the VAD utility no longer imports FastAPI. The trimming/export path for non-empty segments is unchanged.
  • backend/routers/speech_profile.py: the upload route now owns the FastAPI translation by catching VADEmptyError and preserving the existing 400 / "Audio is empty" API response after the existing decode, sample-rate, and duration guards.
  • backend/scripts/stt/j_apply_vad_to_speech_profiles.py: the maintenance script catches VADEmptyError, logs the affected uid, and returns from single(), so one empty profile no longer crashes the batch before later users are processed.
  • backend/tests/unit/test_speech_profile_wav_decode.py: the new test patches apply_vad_for_speech_profile to raise the VAD-empty exception and asserts the route returns the expected 400 without uploading, which covers the previously noted user-facing boundary.

Validation: backend CI is green. I also ran py_compile, git diff --check, and an AST/static check confirming the utility has no FastAPI import, defines/raises VADEmptyError, and both callers catch it. The focused local pytest collection is still blocked by this checkout’s missing backend test dependency (google via tests/unit/conftest.py), so I’m relying on the green hermetic backend CI for the runtime test signal.

This still looks like a clean backend separation-of-concerns cleanup, and the existing backend + positive-signal labels fit. No maintainer escalation from me beyond the normal final merge decision.


by AI on behalf of David — automated maintainer review; final merge decisions remain with the maintainers.

@undivisible
undivisible force-pushed the fix-vad-http-exception-coupling-5064561204901573843 branch from 4e1665c to 5466314 Compare August 10, 2026 21:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_892655a0-1a99-445d-ad8c-1c9f5ad4013d)

@undivisible
undivisible force-pushed the fix-vad-http-exception-coupling-5064561204901573843 branch from 5466314 to 84043ba Compare August 10, 2026 23:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_20ce6641-379a-4e88-b712-b418987d762b)

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this looks like a good narrow backend cleanup. I reviewed the current head (84043baef77fa1e23442ad297539e9fc2ec8f9fa):

  • backend/utils/stt/vad.py: replacing the FastAPI HTTPException with VADEmptyError keeps the VAD utility framework-independent while preserving the empty-audio signal before any trimming/export work.
  • backend/routers/speech_profile.py: catching VADEmptyError at the upload route preserves the user-facing 400 "Audio is empty" response and still bails out before duration caching, upload, or embedding extraction.
  • backend/scripts/stt/j_apply_vad_to_speech_profiles.py: handling VADEmptyError lets the maintenance script skip empty profiles instead of crashing on a web-layer exception, which matches the decoupling goal.
  • backend/tests/unit/test_speech_profile_wav_decode.py: the added empty-VAD test asserts the 400 response and verifies upload is not called, covering the earlier automated concern around this route behavior.

CI is green, and I do not see a blocking issue in this diff. Keeping this as a positive signal; final merge judgment can stay with the human maintainer path already in progress.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@cursor

cursor Bot commented Aug 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ceb5afbc-f18d-4c7f-bd6a-208ae658e701)

@mintlify

mintlify Bot commented Aug 11, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
omi 🟢 Ready View Preview Aug 11, 2026, 4:08 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3e4715d46

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/database/action_items.py Outdated
Comment on lines +137 to +139
if isinstance(value, str) and value:
try:
action_item_data[date_field] = datetime.fromisoformat(value.replace('Z', '+00:00'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize action-item dates to UTC before Firestore writes

When a tool or LLM supplies a date-only ISO string such as 2024-01-01, datetime.fromisoformat produces a timezone-naive value; an already-parsed naive datetime bypasses this branch entirely. Those values are then handed to Firestore, which rejects them, causing extracted action items—potentially an entire background batch—to be lost. Convert both string and datetime inputs to timezone-aware UTC before returning.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment on lines +4 to +6
# Manual only: automatic push/schedule tagging was cutting a new macOS candidate
# tag on nearly every desktop-affecting main merge and every 15 minutes. Plan and
# publish a candidate deliberately via workflow_dispatch; qualification/promotion

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore an automatic desktop release trigger

After this edit, desktop_auto_release.yml has only workflow_dispatch, so ordinary merges never invoke the planner and no daily beta candidate is cut unless an operator remembers to run the workflow manually. Restore the scheduled automatic trigger required by the repository's desktop release pipeline.

AGENTS.md reference: AGENTS.md:L126-L128

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment thread backend/routers/users.py Outdated
Comment on lines 484 to 487
if url == '' or url == ',':
disable_user_webhook_db(uid, wtype)
else:
enable_user_webhook_db(uid, wtype)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable cleared audio webhooks regardless of retained delay

When a mobile user clears the audio-bytes webhook and saves, developer_mode_provider.dart posts ,<delay> (normally ,5), which does not match either literal here. The endpoint therefore re-enables a webhook with no URL, so the toggle comes back on and audio processing remains enabled despite the user's attempt to disable it; determine emptiness from the URL portion before the comma.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment on lines +35 to +36
String? selectedLanguageName = selectedLanguage != null
? homeProvider.availableLanguages.entries.firstWhere((element) => element.value == selectedLanguage).key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve unknown stored languages when opening the picker

If the stored primary-language code is not in this bundled map, opening the forced language dialog throws StateError at firstWhere before it can render. This is reachable for valid server-supported values such as sw, cy, or af, which PATCH /v1/users/language accepts but the bundled map omits; use the existing safe lookup/fallback instead of assuming every stored code is present.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment thread web/app/src/hooks/useRecording.ts Outdated
Comment on lines +101 to +109
setSegments((prev) => {
// Update existing segment or add new one
const existingIndex = prev.findIndex((s) => s.id === segment.id);
if (existingIndex >= 0) {
const updated = [...prev];
updated[existingIndex] = segment;
return updated;
}
return [...prev, segment];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the live transcript segment list

During long browser recordings, every finalized segment takes this append path, so the React state and rendered transcript grow without limit while each update copies the entire array. Sessions reaching hundreds of segments progressively stall Chrome and can make the recording UI unusable; retain only a bounded recent window while leaving the server-side session intact for finalization.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment thread backend/routers/mcp.py Outdated
Comment on lines 545 to 547
conversation_ids = vector_db.query_vectors(query, uid, starts_at=starts_at, ends_at=ends_at, k=limit)
if not conversation_ids:
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Search transcript chunks in MCP conversation search

This route now queries only conversation summary vectors, which do not contain phrases spoken solely in transcript segments. Consequently, MCP clients receive no result for exact names, decisions, or quotes that were omitted from the generated summary even though matching transcript chunks are indexed; merge search_transcript_chunks hits with the summary results and return the corresponding snippets.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment on lines +1030 to +1032
// Content-derived hit region: the fixed window is larger than the
// visible chrome/menu, and its transparent margins must keep passing
// clicks through to windows below (hitTest returns nil outside this).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass clicks through the notch panel's transparent margins

In notch mode the panel is intentionally fixed at its maximum hover size, leaving a large transparent area around the visible chrome. Returning nil from the content view's hitTest does not make the NSWindow click-through—the frame view still owns the event—and this change removes the window-level ignoresMouseEvents synchronization, so the invisible panel intercepts clicks on the main window's top navigation and other apps beneath it. Restore window-level mouse interception control or stop reserving the oversized transparent frame.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment thread backend/utils/retrieval/rag.py Outdated
Comment on lines +129 to +132
]
for f in futures:
f.result()
ordered_chunks = [context_data[m.id] for m in memories if m.id in context_data]
context_str = '\n'.join(ordered_chunks).strip()
context_str = '\n'.join(context_data.values()).strip()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve ranked order when assembling RAG context

Each worker inserts its chunk into context_data when it finishes, so joining dict.values() orders context by nondeterministic thread completion rather than the similarity-ranked memories list established above. With multiple retrieved conversations this can present the LLM with arbitrarily reordered evidence and produce unstable or less relevant answers; rebuild the output by iterating memories and selecting matching IDs instead of deferring the known defect in an untracked TODO.

AGENTS.md reference: AGENTS.md:L93-L93

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment thread app/lib/env/prod_env.dart Outdated
Comment on lines +11 to +13
@override
@EnviedField(varName: 'OPENAI_API_KEY', obfuscate: true)
final String? openAIAPIKey = _ProdEnv.openAIAPIKey;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the OpenAI server key out of the mobile binary

The production Codemagic app workflows write the real OPENAI_API_KEY into .env, and this EnviedField causes build_runner to compile that value into every released Flutter binary. obfuscate: true is reversible obfuscation rather than secret storage, so an app recipient can recover the provider credential and use it outside Omi; this also directly contradicts app/config/client_env_policy.yaml, which classifies OPENAI_API_KEY as server-only. Route OpenAI calls through an authenticated backend and remove the field from the public client.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

Comment on lines 209 to 212
async def initialize_stt(self) -> bool:
request = self.host.request
provider = getattr(self.host.stt_service, 'value', self.host.stt_service)
if self.host.use_custom_stt:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Attribute fallback STT failures to the serving provider

When a session starts with Parakeet but _create_stt_socket falls back to Modulate, that method updates self.host.stt_service after this local value has already been captured. The death monitor and initialization failure paths therefore report parakeet in the client failure event and provider metrics even though Modulate was serving and failed, obscuring the actual incident and misleading provider-specific diagnostics; resolve the provider after socket creation or at each failure boundary.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope: this file is not part of this PR's diff. The branch was reset to the VAD-only exception-decoupling change (per the maintainer review), and the tree-wide diff that surfaced this file was removed.

@Git-on-my-level Git-on-my-level added needs-scope-reduction PR scope should be reduced or split security-review Touches auth, provider routing, secrets, or security-sensitive surfaces workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior and removed backend Backend Task (python) positive-signal Good PR — positive signal, not a formal approval labels Aug 11, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the original VAD cleanup — the small VADEmptyError part still looks directionally right (backend/utils/stt/vad.py, backend/routers/speech_profile.py, and backend/scripts/stt/j_apply_vad_to_speech_profiles.py keep the framework exception at the route/script boundary).

I need to request changes on the current head, though, because it has grown far beyond that refactor and now carries several unrelated, high-risk behavior changes:

  • app/lib/backend/http/openai.dart adds direct client calls to https://api.openai.com/v1/... with Authorization: Bearer ${Env.openAIAPIKey}, and app/.env.template / app/lib/env/*.dart add an app-side OpenAI key. That moves LLM calls and user content from the backend-managed/gateway path into the shipped app surface, which needs an explicit security/privacy/product decision rather than being bundled into a VAD utility cleanup.
  • backend/routers/desktop_chat.py removes the structured managed lane (CHAT_STRUCTURED_AUTO_LANE_ID, _MANAGED_STRUCTURED_ALIASES, lane-specific accounting) and routes managed gateway requests through the chat-agent lane. That can change model routing/personality/accounting for non-conversational extraction/planner calls.
  • backend/route_policy_manifest.yaml plus routers such as backend/routers/conversations.py, backend/routers/knowledge_graph.py, backend/routers/integrations.py, backend/routers/memories.py, and backend/routers/users.py remove multiple first-party extraction/synthesis/language endpoints and their policy entries, while many corresponding tests are deleted. That is a broad API/product contract change, not a decoupling refactor.
  • .github/workflows/desktop_auto_release.yml, .github/scripts/check-desktop-changelog.py, .github/scripts/plan-desktop-release.py, and their tests remove scheduled release-train/changelog/fallback behavior. This is release infrastructure and needs focused workflow review on its own.
  • desktop/macos/AGENTS.md changes AI/coding-agent release-pipeline guidance, but the new text says candidates are cut on every macOS-affecting merge plus a 15-minute schedule, while the workflow diff removes the schedule and leaves workflow_dispatch only. That instruction file can directly mislead future coding/review agents about how desktop releases work, so it should not land in this state.
  • backend/database/vector_db.py removes the injectable query_vector path and weakens transcript date filtering to only apply when both bounds are present. That changes retrieval behavior and testability independently of the VAD work.
  • web/app/package.json / web/app/package-lock.json remove Vitest and test scripts while web transcript tests are deleted, which further broadens the risk surface.

Please reduce this PR back to the VAD exception cleanup, or split/rework these unrelated backend API, desktop release, client-LLM, retrieval, web dependency, and agent-instruction changes into separately reviewed PRs with their own validation. The current head needs security-sensitive client/API-key review and release-workflow maintainer sign-off before it can be considered.


by AI on behalf of David — automated maintainer review; blocking here because the current head bundles unrelated security-sensitive client LLM/API-key changes, release workflow changes, and inaccurate agent-facing release guidance.

undivisible and others added 4 commits August 12, 2026 16:40
Refactored `apply_vad_for_speech_profile` in `backend/utils/stt/vad.py` to raise a new `VADEmptyError` instead of FastAPI's `HTTPException(400)`. This logic is now caught in the `backend/routers/speech_profile.py` router and `backend/scripts/stt/j_apply_vad_to_speech_profiles.py` script, decoupling web exceptions from core domain utilities.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Exercise the real VAD zero-segment exception at the utility boundary and cover the HTTP and batch translations at their callers.

Failure-Class: none

Tests: backend/.venv/bin/python -m pytest -q tests/unit/test_speech_profile_wav_decode.py tests/unit/test_vad_onnx.py
Exercise the production VAD zero-segment exception through the upload route and batch maintenance script, asserting both user-facing translations and their side-effect guards.

Failure-Class: none
@undivisible
undivisible force-pushed the fix-vad-http-exception-coupling-5064561204901573843 branch from a3e4715 to ba4e39e Compare August 12, 2026 08:44
@cursor

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_a49a248c-0bf3-4aa5-bf22-ba9a46d46cb5)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba4e39e538

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

) as mock_upload, patch.object(
vad_mod, "vad_is_empty", return_value=[]
) as mock_vad:
batch_mod.execute()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the batch test propagate worker exceptions

Fresh evidence since the earlier coverage comments is that this added test invokes the failure path through threading.Thread: if the new except VADEmptyError block is removed or mistyped, the exception terminates only the worker, join() still returns, and both assertions still pass (pytest merely reports an unhandled-thread warning by default). Run the worker synchronously through a controllable seam or otherwise assert that no worker exception occurred so the regression test actually protects the batch exception boundary.

AGENTS.md reference: AGENTS.md:L43-L45

Useful? React with 👍 / 👎.

@Git-on-my-level Git-on-my-level added backend Backend Task (python) positive-signal Good PR — positive signal, not a formal approval and removed needs-scope-reduction PR scope should be reduced or split security-review Touches auth, provider routing, secrets, or security-sensitive surfaces workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior labels Aug 12, 2026
@Git-on-my-level
Git-on-my-level dismissed their stale review August 12, 2026 10:08

Resolved on current head: prior unrelated client-LLM/API-key, release workflow, API-removal, retrieval, web-test, and agent-instruction changes are no longer present.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for narrowing this back down. I reviewed the current head (ba4e39e53854fc573e142dd21af031d6a3f87f03), and the earlier blocking concerns about unrelated client-LLM/API-key, release workflow, API-removal, retrieval, web-test, and agent-instruction changes appear resolved on this head.

File-by-file notes:

  • backend/utils/stt/vad.py: apply_vad_for_speech_profile() now raises a domain-specific VADEmptyError instead of importing/raising FastAPI's HTTPException, so the VAD utility is no longer coupled to the web layer while preserving the empty-audio signal before trimming/export.
  • backend/routers/speech_profile.py: the upload route catches VADEmptyError and translates it back to the existing 400 "Audio is empty" API response, so client-facing behavior remains appropriate at the FastAPI boundary.
  • backend/scripts/stt/j_apply_vad_to_speech_profiles.py: the batch speech-profile script now treats empty VAD results as a per-user skip instead of letting a web-framework exception crash the worker path, which is the right separation for this script context.
  • backend/tests/unit/test_speech_profile_wav_decode.py: the new coverage exercises both the route translation path and the batch-script skip path, and keeps the upload side-effect guarded when VAD returns no segments.
  • backend/tests/unit/test_vad_onnx.py: the new unit test verifies the utility-level contract directly: zero VAD segments raise VADEmptyError.

Validation I ran locally: focused backend unit suite for test_speech_profile_wav_decode.py and test_vad_onnx.py — 34 passed.

I’m dismissing my stale blocking review from the broader previous head and leaving this as a positive signal rather than formal approval because the cached approval guard for this run disallows auto-approval.


by AI on behalf of David — automated maintainer review; leaving a positive signal after verifying the previous broad-scope/security/workflow concerns are no longer present on this narrowed backend cleanup.

…n-coupling-5064561204901573843

# Conflicts:
#	backend/utils/stt/vad.py
@cursor

cursor Bot commented Aug 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_231e5518-4166-48ee-9bfe-3a3811397e4f)

@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_db2f1de8-786b-465e-83e6-99632c77241b)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aeb9888a78

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +75 to +76
from scripts.stt import j_apply_vad_to_speech_profiles as batch_mod
from utils.stt import vad as vad_mod

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Import production modules outside the stub window

When backend tests run in the supported shared-process mode (BACKEND_PYTEST_FILE_ISOLATION=0), these imports cache batch_mod and the real vad_mod while database and storage dependencies resolve to _AutoMock; restoring sys.modules afterward does not repair those modules' captured references. Later tests therefore receive production modules permanently bound to fake Redis/storage functions, making results collection-order-dependent. Import these modules outside this module-scope stub window or replace the stubs through an isolated fixture.

AGENTS.md reference: backend/AGENTS.md:L216-L216

Useful? React with 👍 / 👎.

@undivisible
undivisible merged commit cc21201 into main Aug 14, 2026
38 checks passed
@undivisible
undivisible deleted the fix-vad-http-exception-coupling-5064561204901573843 branch August 14, 2026 03:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI backend Backend Task (python) positive-signal Good PR — positive signal, not a formal approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants